@yaoyuanchao/dingtalk 1.3.1 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yaoyuanchao/dingtalk",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "type": "module",
5
5
  "description": "DingTalk channel plugin for Clawdbot with Stream Mode support",
6
6
  "license": "MIT",
package/src/api.ts CHANGED
@@ -93,12 +93,16 @@ export async function getDingTalkAccessToken(clientId: string, clientSecret: str
93
93
  export async function sendViaSessionWebhook(
94
94
  sessionWebhook: string,
95
95
  text: string,
96
- ): Promise<{ ok: boolean }> {
96
+ ): Promise<{ ok: boolean; errcode?: number; errmsg?: string }> {
97
97
  const res = await jsonPost(sessionWebhook, {
98
98
  msgtype: "text",
99
99
  text: { content: text },
100
100
  });
101
- return { ok: res?.errcode === 0 || !res?.errcode };
101
+ const ok = res?.errcode === 0 || !res?.errcode;
102
+ if (!ok) {
103
+ console.warn(`[dingtalk] SessionWebhook text error: errcode=${res?.errcode}, errmsg=${res?.errmsg}`);
104
+ }
105
+ return { ok, errcode: res?.errcode, errmsg: res?.errmsg };
102
106
  }
103
107
 
104
108
  /** Send markdown via sessionWebhook */
@@ -106,12 +110,16 @@ export async function sendMarkdownViaSessionWebhook(
106
110
  sessionWebhook: string,
107
111
  title: string,
108
112
  text: string,
109
- ): Promise<{ ok: boolean }> {
113
+ ): Promise<{ ok: boolean; errcode?: number; errmsg?: string }> {
110
114
  const res = await jsonPost(sessionWebhook, {
111
115
  msgtype: "markdown",
112
116
  markdown: { title, text },
113
117
  });
114
- return { ok: res?.errcode === 0 || !res?.errcode };
118
+ const ok = res?.errcode === 0 || !res?.errcode;
119
+ if (!ok) {
120
+ console.warn(`[dingtalk] SessionWebhook markdown error: errcode=${res?.errcode}, errmsg=${res?.errmsg}`);
121
+ }
122
+ return { ok, errcode: res?.errcode, errmsg: res?.errmsg };
115
123
  }
116
124
 
117
125
  /** Send image via sessionWebhook using markdown format */
package/src/channel.ts CHANGED
@@ -206,10 +206,11 @@ export const dingtalkPlugin = {
206
206
  const account = resolveDingTalkAccount({ cfg, accountId });
207
207
  const [type, id] = to.split(':');
208
208
 
209
- // Build text message with image URL
209
+ // Build text message with image URL as markdown image
210
+ const imageMarkdown = `![image](${mediaUrl})`;
210
211
  const textMessage = text
211
- ? `${text}\n\n图片链接: ${mediaUrl}`
212
- : `图片链接: ${mediaUrl}`;
212
+ ? `${text}\n\n${imageMarkdown}`
213
+ : imageMarkdown;
213
214
 
214
215
  if (type === 'dm') {
215
216
  await sendDingTalkRestMessage({
package/src/monitor.ts CHANGED
@@ -577,9 +577,13 @@ async function processInboundMessage(
577
577
  cfg: actualCfg,
578
578
  dispatcherOptions: {
579
579
  deliver: async (payload: any) => {
580
- if (payload.text) {
581
- await deliverReply(replyTarget, payload.text, log);
580
+ log?.info?.("[dingtalk] Deliver payload keys: " + Object.keys(payload || {}).join(',') + " text?=" + (typeof payload?.text) + " markdown?=" + (typeof payload?.markdown));
581
+ const textToSend = resolveDeliverText(payload, log);
582
+ if (textToSend) {
583
+ await deliverReply(replyTarget, textToSend, log);
582
584
  setStatus?.({ lastOutboundAt: Date.now() });
585
+ } else {
586
+ log?.info?.("[dingtalk] Deliver: no text resolved from payload");
583
587
  }
584
588
  },
585
589
  onError: (err: any) => {
@@ -679,8 +683,12 @@ async function dispatchWithFullPipeline(params: {
679
683
  responsePrefix: '',
680
684
  deliver: async (payload: any) => {
681
685
  try {
682
- const textToSend = payload.markdown || payload.text;
683
- if (!textToSend) return { ok: true };
686
+ log?.info?.("[dingtalk] Pipeline deliver payload keys: " + Object.keys(payload || {}).join(',') + " text?=" + (typeof payload?.text) + " markdown?=" + (typeof payload?.markdown));
687
+ const textToSend = resolveDeliverText(payload, log);
688
+ if (!textToSend) {
689
+ log?.info?.("[dingtalk] Pipeline deliver: no text resolved from payload");
690
+ return { ok: true };
691
+ }
684
692
  await deliverReply(replyTarget, textToSend, log);
685
693
  setStatus?.({ lastOutboundAt: Date.now() });
686
694
  return { ok: true };
@@ -702,6 +710,32 @@ async function dispatchWithFullPipeline(params: {
702
710
  rt.channel?.activity?.record?.('dingtalk', account.accountId, 'message');
703
711
  }
704
712
 
713
+ /**
714
+ * Extract text + media URL from a deliver payload.
715
+ * The Clawdbot platform may send media URLs in separate fields (e.g. from the `message` tool).
716
+ * We merge them into the text as markdown image syntax so DingTalk can render them.
717
+ */
718
+ function resolveDeliverText(payload: any, log?: any): string | undefined {
719
+ // payload.markdown may be a boolean flag (not the actual text), so check type
720
+ let text = (typeof payload.markdown === 'string' && payload.markdown) || payload.text;
721
+
722
+ // Guard: ensure text is a string (platform might send unexpected types)
723
+ if (text != null && typeof text !== 'string') {
724
+ log?.info?.("[dingtalk] Deliver payload has non-string text type=" + typeof text + ", payload keys=" + Object.keys(payload).join(','));
725
+ text = String(text);
726
+ }
727
+
728
+ const mediaUrl = payload.mediaUrl || payload.media || payload.imageUrl || payload.image;
729
+
730
+ if (mediaUrl && typeof mediaUrl === 'string' && mediaUrl.startsWith('http')) {
731
+ log?.info?.("[dingtalk] Deliver payload includes media URL: " + mediaUrl);
732
+ const imageMarkdown = `![image](${mediaUrl})`;
733
+ text = text ? `${text}\n\n${imageMarkdown}` : imageMarkdown;
734
+ }
735
+
736
+ return text || undefined;
737
+ }
738
+
705
739
  async function deliverReply(target: any, text: string, log?: any): Promise<void> {
706
740
  const now = Date.now();
707
741
  const chunkLimit = 2000;
@@ -743,13 +777,17 @@ async function deliverReply(target: any, text: string, log?: any): Promise<void>
743
777
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
744
778
  try {
745
779
  log?.info?.("[dingtalk] Using sessionWebhook (attempt " + attempt + "/" + maxRetries + "), format=" + messageFormat);
746
- log?.info?.("[dingtalk] Sending text: " + chunk.substring(0, 200));
780
+ log?.info?.("[dingtalk] Sending text (" + chunk.length + " chars): " + chunk.substring(0, 200));
781
+ let sendResult: { ok: boolean; errcode?: number; errmsg?: string };
747
782
  if (isMarkdown) {
748
- await sendMarkdownViaSessionWebhook(target.sessionWebhook, "Reply", chunk);
783
+ sendResult = await sendMarkdownViaSessionWebhook(target.sessionWebhook, "Reply", chunk);
749
784
  } else {
750
- await sendViaSessionWebhook(target.sessionWebhook, chunk);
785
+ sendResult = await sendViaSessionWebhook(target.sessionWebhook, chunk);
786
+ }
787
+ if (!sendResult.ok) {
788
+ throw new Error(`SessionWebhook rejected: errcode=${sendResult.errcode}, errmsg=${sendResult.errmsg}`);
751
789
  }
752
- log?.info?.("[dingtalk] SessionWebhook send OK");
790
+ log?.info?.("[dingtalk] SessionWebhook send OK (errcode=" + (sendResult.errcode ?? 0) + ")");
753
791
  webhookSuccess = true;
754
792
  break;
755
793
  } catch (err) {